<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>JVM系列-第4章-虚拟机栈 | 风祈的时光录</title><meta name="keywords" content="JVM，虚拟机。"><meta name="author" content="youthlql"><meta name="copyright" content="youthlql"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="JVM系列-第4章-虚拟机栈。">
<meta property="og:type" content="article">
<meta property="og:title" content="JVM系列-第4章-虚拟机栈">
<meta property="og:url" content="https://imlql.cn/post/5b1b6560.html">
<meta property="og:site_name" content="风祈的时光录">
<meta property="og:description" content="JVM系列-第4章-虚拟机栈。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png">
<meta property="article:published_time" content="2020-11-10T02:38:42.000Z">
<meta property="article:modified_time" content="2022-07-24T11:02:09.800Z">
<meta property="article:author" content="youthlql">
<meta property="article:tag" content="JVM">
<meta property="article:tag" content="虚拟机">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png"><link rel="shortcut icon" href="https://npm.elemecdn.com/youthlql@1.0.8/blog/favicon.png"><link rel="canonical" href="https://imlql.cn/post/5b1b6560"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//hm.baidu.com"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://npm.elemecdn.com/@fortawesome/fontawesome-free@6/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?f693ff99cc7e613b88cf5b729a14b48b";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'JVM系列-第4章-虚拟机栈',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-07-24 19:02:09'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/ali_icon.css"><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/2-24-mogai.css"><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/atom.xml" title="风祈的时光录" type="application/atom+xml">
</head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/avatar/2.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data is-center"><div class="data-item"><a href="/archives/"><div class="headline">文章</div><div class="length-num">37</div></a></div><div class="data-item"><a href="/tags/"><div class="headline">标签</div><div class="length-num">21</div></a></div><div class="data-item"><a href="/categories/"><div class="headline">分类</div><div class="length-num">13</div></a></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">风祈的时光录</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">JVM系列-第4章-虚拟机栈</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2020-11-10T02:38:42.000Z" title="发表于 2020-11-10 10:38:42">2020-11-10</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-07-24T11:02:09.800Z" title="更新于 2022-07-24 19:02:09">2022-07-24</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/JVM/">JVM</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/JVM/1-%E5%86%85%E5%AD%98%E4%B8%8E%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AF%87/">1.内存与垃圾回收篇</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">10.3k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>36分钟</span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="虚拟机栈"><a href="#虚拟机栈" class="headerlink" title="虚拟机栈"></a>虚拟机栈</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><h3 id="虚拟机栈的出现背景"><a href="#虚拟机栈的出现背景" class="headerlink" title="虚拟机栈的出现背景"></a>虚拟机栈的出现背景</h3><ol>
<li> 由于跨平台性的设计，Java的指令都是根据栈来设计的。不同平台CPU架构不同，所以不能设计为基于寄存器的【如果设计成基于寄存器的，耦合度高，性能会有所提升，因为可以对具体的CPU架构进行优化，但是跨平台性大大降低】。</li>
<li> 优点是跨平台，指令集小，编译器容易实现，缺点是性能下降，实现同样的功能需要更多的指令。</li>
</ol>
<h3 id="内存中的栈与堆"><a href="#内存中的栈与堆" class="headerlink" title="内存中的栈与堆"></a>内存中的栈与堆</h3><ol>
<li>首先栈是运行时的单位，而堆是存储的单位。</li>
<li>即：栈解决程序的运行问题，即程序如何执行，或者说如何处理数据。堆解决的是数据存储的问题，即数据怎么放，放哪里</li>
</ol>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0001.png">



<h3 id="虚拟机栈基本内容"><a href="#虚拟机栈基本内容" class="headerlink" title="虚拟机栈基本内容"></a>虚拟机栈基本内容</h3><ul>
<li><p>Java虚拟机栈是什么？</p>
<ul>
<li>Java虚拟机栈（Java Virtual Machine Stack），早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈，其内部保存一个个的栈帧（Stack Frame），<strong>对应着一次次的Java方法调用</strong>，栈是线程私有的</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StackTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        StackTest test = <span class="keyword">new</span> StackTest();</span><br><span class="line">        test.methodA();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">methodA</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> i = <span class="number">10</span>;</span><br><span class="line">        <span class="keyword">int</span> j = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line">        methodB();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">methodB</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">int</span> k = <span class="number">30</span>;</span><br><span class="line">        <span class="keyword">int</span> m = <span class="number">40</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></li>
</ul>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0002.png">

<ul>
<li><p>虚拟机栈的生命周期</p>
<ul>
<li>生命周期和线程一致，也就是线程结束了，该虚拟机栈也销毁了</li>
</ul>
</li>
<li><p>虚拟机栈的作用</p>
<ul>
<li>主管Java程序的运行，它保存方法的局部变量（8 种基本数据类型、对象的引用地址）、部分结果，并参与方法的调用和返回。</li>
<li>局部变量，它是相比于成员变量来说的（或属性）</li>
<li>基本数据类型变量 VS 引用类型变量（类、数组、接口）</li>
</ul>
</li>
</ul>
<h3 id="虚拟机栈的特点"><a href="#虚拟机栈的特点" class="headerlink" title="虚拟机栈的特点"></a>虚拟机栈的特点</h3><ul>
<li><p>栈是一种快速有效的分配存储方式，访问速度仅次于程序计数器。</p>
</li>
<li><p>JVM直接对Java栈的操作只有两个：</p>
<ul>
<li>每个方法执行，伴随着<strong>进栈</strong>（入栈、压栈）</li>
<li>执行结束后的<strong>出栈</strong>工作</li>
</ul>
</li>
<li><p>对于栈来说不存在垃圾回收问题</p>
<ul>
<li>栈不需要GC，但是可能存在OOM</li>
</ul>
</li>
</ul>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0003.png">

<h3 id="虚拟机栈的异常"><a href="#虚拟机栈的异常" class="headerlink" title="虚拟机栈的异常"></a>虚拟机栈的异常</h3><p><strong>面试题：栈中可能出现的异常？</strong></p>
<ul>
<li><p>Java 虚拟机规范允许Java栈的大小是动态的或者是固定不变的。</p>
<ul>
<li><p>如果采用固定大小的Java虚拟机栈，那每一个线程的Java虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量，Java虚拟机将会抛出一个<strong>StackoverflowError</strong> 异常。</p>
</li>
<li><p>如果Java虚拟机栈可以动态扩展，并且在尝试扩展的时候无法申请到足够的内存，或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈，那Java虚拟机将会抛出一个 <strong>OutofMemoryError</strong> 异常。</p>
</li>
</ul>
</li>
</ul>
<h3 id="设置栈内存大小"><a href="#设置栈内存大小" class="headerlink" title="设置栈内存大小"></a>设置栈内存大小</h3><h4 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h4><blockquote>
<p>多去官方文档看看：<a target="_blank" rel="noopener" href="https://docs.oracle.com/en/java/javase/11/tools/java.html#GUID-3B1CE181-CD30-4178-9602-230B800D4FAE">https://docs.oracle.com/en/java/javase/11/tools/java.html#GUID-3B1CE181-CD30-4178-9602-230B800D4FAE</a></p>
<p>地址经常变</p>
</blockquote>
<p>我们可以使用参数 <strong>-Xss</strong> 选项来设置线程的最大栈空间，栈的大小直接决定了函数调用的最大可达深度。</p>
<blockquote>
<p>Sets the thread stack size (in bytes). Append the letter <code>k</code> or <code>K</code> to indicate KB, <code>m</code> or <code>M</code> to indicate MB, and <code>g</code> or <code>G</code> to indicate GB. The default value depends on the platform:</p>
<ul>
<li>Linux/x64 (64-bit): 1024 KB</li>
<li>macOS (64-bit): 1024 KB</li>
<li>Oracle Solaris/x64 (64-bit): 1024 KB</li>
<li>Windows: The default value depends on virtual memory</li>
</ul>
</blockquote>
<p>The following examples set the thread stack size to 1024 KB in different units:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">-Xss1m</span><br><span class="line">-Xss1024k</span><br><span class="line">-Xss1048576</span><br></pre></td></tr></table></figure>

<h4 id="举例"><a href="#举例" class="headerlink" title="举例"></a>举例</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StackErrorTest</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> count = <span class="number">1</span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        System.out.println(count);</span><br><span class="line">        count++;</span><br><span class="line">        main(args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>没设置参数前</strong></p>
<p>部分输出结果：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="number">11404</span></span><br><span class="line"><span class="number">11405</span></span><br><span class="line"><span class="number">11406</span></span><br><span class="line">Exception in thread <span class="string">&quot;main&quot;</span> java.lang.StackOverflowError</span><br><span class="line">	at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:<span class="number">691</span>)</span><br></pre></td></tr></table></figure>

<p>说明栈在11406这个深度溢出了</p>
<p><strong>设置栈参数之后</strong></p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0004.png">

<p>部分输出结果</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="number">2474</span></span><br><span class="line"><span class="number">2475</span></span><br><span class="line"><span class="number">2476</span></span><br><span class="line">Exception in thread <span class="string">&quot;main&quot;</span> java.lang.StackOverflowError</span><br><span class="line">	at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:<span class="number">77</span>)</span><br></pre></td></tr></table></figure>

<p>说明参数起作用了</p>
<h2 id="栈的存储单位"><a href="#栈的存储单位" class="headerlink" title="栈的存储单位"></a>栈的存储单位</h2><h3 id="栈中存储什么？"><a href="#栈中存储什么？" class="headerlink" title="栈中存储什么？"></a>栈中存储什么？</h3><ol>
<li> 每个线程都有自己的栈，栈中的数据都是以<strong>栈帧</strong>（Stack Frame）的格式存在</li>
<li> 在这个线程上正在执行的每个方法都各自对应一个栈帧（Stack Frame）。</li>
<li> 栈帧是一个内存区块，是一个数据集，维系着方法执行过程中的各种数据信息。</li>
</ol>
<h3 id="栈运行原理"><a href="#栈运行原理" class="headerlink" title="栈运行原理"></a>栈运行原理</h3><ol>
<li><p>JVM直接对Java栈的操作只有两个，就是对栈帧的<strong>压栈和出栈</strong>，遵循先进后出（后进先出）原则</p>
</li>
<li><p> 在一条活动线程中，一个时间点上，只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧（栈顶栈帧）是有效的。这个栈帧被称为<strong>当前栈帧（Current Frame）</strong>，与当前栈帧相对应的方法就是<strong>当前方法（Current Method）</strong>，定义这个方法的类就是<strong>当前类（Current Class）</strong></p>
</li>
<li><p>执行引擎运行的所有字节码指令只针对当前栈帧进行操作。</p>
</li>
<li><p>如果在该方法中调用了其他方法，对应的新的栈帧会被创建出来，放在栈的顶端，成为新的当前帧。</p>
</li>
</ol>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0005.png">

<ol>
<li> <strong>不同线程中所包含的栈帧是不允许存在相互引用的</strong>，即不可能在一个栈帧之中引用另外一个线程的栈帧。</li>
<li> 如果当前方法调用了其他方法，方法返回之际，当前栈帧会传回此方法的执行结果给前一个栈帧，接着，虚拟机会丢弃当前栈帧，使得前一个栈帧重新成为当前栈帧。</li>
<li>Java方法有两种返回函数的方式。<ul>
<li>一种是正常的函数返回，使用return指令。</li>
<li>另一种是方法执行中出现未捕获处理的异常，以抛出异常的方式结束。</li>
<li>但不管使用哪种方式，都会导致栈帧被弹出。</li>
</ul>
</li>
</ol>
<h3 id="栈帧的内部结构"><a href="#栈帧的内部结构" class="headerlink" title="栈帧的内部结构"></a>栈帧的内部结构</h3><p>每个栈帧中存储着：</p>
<ul>
<li><p>局部变量表（Local Variables）</p>
</li>
<li><p>操作数栈（Operand Stack）（或表达式栈）</p>
</li>
<li><p>动态链接（Dynamic Linking）（或指向运行时常量池的方法引用）</p>
</li>
<li><p>方法返回地址（Return Address）（或方法正常退出或者异常退出的定义）</p>
</li>
<li><p>一些附加信息</p>
</li>
</ul>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0006.png">

<p>并行每个线程下的栈都是私有的，因此每个线程都有自己各自的栈，并且每个栈里面都有很多栈帧，栈帧的大小主要由局部变量表 和 操作数栈决定的</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0007.png">

<h2 id="局部变量表"><a href="#局部变量表" class="headerlink" title="局部变量表"></a>局部变量表</h2><h3 id="认识局部变量表"><a href="#认识局部变量表" class="headerlink" title="认识局部变量表"></a>认识局部变量表</h3><h4 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h4><ol>
<li> 局部变量表也被称之为局部变量数组或本地变量表</li>
<li> <strong>定义为一个数字数组，主要用于存储方法参数和定义在方法体内的局部变量</strong>，这些数据类型包括各类基本数据类型、对象引用（reference），以及returnAddress返回值类型。</li>
<li> 由于局部变量表是建立在线程的栈上，是线程的私有数据，因此<strong>不存在数据安全问题</strong></li>
<li> <strong>局部变量表所需的容量大小是在编译期确定下来的</strong>，并保存在方法的Code属性的<strong>maximum local variables</strong>数据项中。在方法运行期间是不会改变局部变量表的大小的。</li>
<li>方法嵌套调用的次数由栈的大小决定。一般来说，栈越大，方法嵌套调用次数越多。<ul>
<li>  对一个函数而言，它的参数和局部变量越多，使得局部变量表膨胀，它的栈帧就越大，以满足方法调用所需传递的信息增大的需求。</li>
<li>  进而函数调用就会占用更多的栈空间，导致其嵌套调用次数就会减少。</li>
</ul>
</li>
<li>局部变量表中的变量只在当前方法调用中有效。<ul>
<li>  在方法执行时，虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。</li>
<li>  当方法调用结束后，随着方法栈帧的销毁，局部变量表也会随之销毁。</li>
</ul>
</li>
</ol>
<h4 id="举例-1"><a href="#举例-1" class="headerlink" title="举例"></a>举例</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LocalVariablesTest</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        LocalVariablesTest test = <span class="keyword">new</span> LocalVariablesTest();</span><br><span class="line">        <span class="keyword">int</span> num = <span class="number">10</span>;</span><br><span class="line">        test.test1();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//练习：</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">testStatic</span><span class="params">()</span></span>&#123;</span><br><span class="line">        LocalVariablesTest test = <span class="keyword">new</span> LocalVariablesTest();</span><br><span class="line">        Date date = <span class="keyword">new</span> Date();</span><br><span class="line">        <span class="keyword">int</span> count = <span class="number">10</span>;</span><br><span class="line">        System.out.println(count);</span><br><span class="line">        <span class="comment">//因为this变量不存在于当前方法的局部变量表中！！</span></span><br><span class="line"><span class="comment">//        System.out.println(this.count);</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//关于Slot的使用的理解</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LocalVariablesTest</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.count = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test1</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Date date = <span class="keyword">new</span> Date();</span><br><span class="line">        String name1 = <span class="string">&quot;atguigu.com&quot;</span>;</span><br><span class="line">        test2(date, name1);</span><br><span class="line">        System.out.println(date + name1);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">test2</span><span class="params">(Date dateP, String name2)</span> </span>&#123;</span><br><span class="line">        dateP = <span class="keyword">null</span>;</span><br><span class="line">        name2 = <span class="string">&quot;songhongkang&quot;</span>;</span><br><span class="line">        <span class="keyword">double</span> weight = <span class="number">130.5</span>;<span class="comment">//占据两个slot</span></span><br><span class="line">        <span class="keyword">char</span> gender = <span class="string">&#x27;男&#x27;</span>;</span><br><span class="line">        <span class="keyword">return</span> dateP + name2;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test3</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.count++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test4</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> a = <span class="number">0</span>;</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">int</span> b = <span class="number">0</span>;</span><br><span class="line">            b = a + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//变量c使用之前已经销毁的变量b占据的slot的位置</span></span><br><span class="line">        <span class="keyword">int</span> c = a + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0008.png">

<p>看完字节码后，可得结论：所以局部变量表所需的容量大小是在编译期确定下来的。</p>
<h4 id="部分详解"><a href="#部分详解" class="headerlink" title="部分详解"></a>部分详解</h4><p>为了更好讲解，我们直接用jclasslib来看字节码，以main方法为例来讲解。一些一目了然的就不讲了</p>
<p>1、0-15  也就是有16行字节码</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0009.png">



<p>2、方法异常信息表</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0010.png">

<p>3、Misc</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0011.png">

<p>4、行号表</p>
<p>Java代码的行号和字节码指令行号的对应关系</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0012.png">



<p>5、注意：生效行数和剩余有效行数都是针对于字节码文件的行数</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0013.png">

<p>1、图中圈的东西表示该局部变量的作用域</p>
<p>2、Start PC==11表示在字节码的11行开始生效，也就是Java代码对应的第15行。而声明int num在java代码的是第14行，说明是从声明的下一行开始生效</p>
<p>3、Length== 5表示局部变量剩余有效行数，main方法字节码指令总共有16行，从11行开始生效，那么剩下就是16-11 ==5。</p>
<p>4、<code>Ljava/lang/String</code>   前面的L表示引用类型</p>
<h3 id="关于Slot的理解"><a href="#关于Slot的理解" class="headerlink" title="关于Slot的理解"></a>关于Slot的理解</h3><ol>
<li> 参数值的存放总是从局部变量数组索引 0 的位置开始，到数组长度-1的索引结束。</li>
<li> 局部变量表，<strong>最基本的存储单元是Slot（变量槽）</strong>，局部变量表中存放编译期可知的各种基本数据类型（8种），引用类型（reference），returnAddress类型的变量。</li>
<li>在局部变量表里，<strong>32位以内的类型只占用一个slot</strong>（包括returnAddress类型），<strong>64位的类型占用两个slot</strong>（1ong和double）。<ul>
<li>byte、short、char在储存前被转换为int，boolean也被转换为int，0表示false，非0表示true</li>
<li>long和double则占据两个slot</li>
</ul>
</li>
<li> JVM会为局部变量表中的每一个Slot都分配一个访问索引，通过这个索引即可成功访问到局部变量表中指定的局部变量值</li>
<li> 当一个实例方法被调用的时候，它的方法参数和方法体内部定义的局部变量将会<strong>按照顺序被复制</strong>到局部变量表中的每一个slot上</li>
<li> 如果需要访问局部变量表中一个64bit的局部变量值时，只需要使用前一个索引即可。（比如：访问long或double类型变量）</li>
<li> 如果当前帧是由构造方法或者实例方法创建的，那么<strong>该对象引用this将会存放在index为0的slot处</strong>，其余的参数按照参数表顺序继续排列。（this也相当于一个变量）</li>
</ol>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0014.png">

<h3 id="Slot代码示例"><a href="#Slot代码示例" class="headerlink" title="Slot代码示例"></a>Slot代码示例</h3><p><strong>this 存放在 index = 0 的位置：</strong></p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test3</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       <span class="keyword">this</span>.count++;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>



<p>局部变量表：this 存放在 index = 0 的位置</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0015.png">



<p><strong>64位的类型（1ong和double）占用两个slot</strong></p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">test2</span><span class="params">(Date dateP, String name2)</span> </span>&#123;</span><br><span class="line">      dateP = <span class="keyword">null</span>;</span><br><span class="line">      name2 = <span class="string">&quot;songhongkang&quot;</span>;</span><br><span class="line">      <span class="keyword">double</span> weight = <span class="number">130.5</span>;<span class="comment">//占据两个slot</span></span><br><span class="line">      <span class="keyword">char</span> gender = <span class="string">&#x27;男&#x27;</span>;</span><br><span class="line">      <span class="keyword">return</span> dateP + name2;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>weight 为 double 类型，index 直接从 3 蹦到了 5</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0016.png">



<p><strong>static 无法调用 this</strong></p>
<p>this 不存在与 static 方法的局部变量表中，所以无法调用</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">testStatic</span><span class="params">()</span></span>&#123;</span><br><span class="line">        LocalVariablesTest test = <span class="keyword">new</span> LocalVariablesTest();</span><br><span class="line">        Date date = <span class="keyword">new</span> Date();</span><br><span class="line">        <span class="keyword">int</span> count = <span class="number">10</span>;</span><br><span class="line">        System.out.println(count);</span><br><span class="line">        <span class="comment">//因为this变量不存在于当前方法的局部变量表中！！</span></span><br><span class="line"><span class="comment">//        System.out.println(this.count);</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>



<h3 id="Slot的重复利用"><a href="#Slot的重复利用" class="headerlink" title="Slot的重复利用"></a>Slot的重复利用</h3><p>栈帧中的局部变量表中的槽位是可以重用的，如果一个局部变量过了其作用域，那么在其作用域之后申明新的局部变量变就很有可能会复用过期局部变量的槽位，从而达到节省资源的目的。</p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test4</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> a = <span class="number">0</span>;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">int</span> b = <span class="number">0</span>;</span><br><span class="line">        b = a + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//变量c使用之前已经销毁的变量b占据的slot的位置</span></span><br><span class="line">    <span class="keyword">int</span> c = a + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>局部变量 c 重用了局部变量 b 的 slot 位置</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0017.png">



<h3 id="静态变量与局部变量的对比"><a href="#静态变量与局部变量的对比" class="headerlink" title="静态变量与局部变量的对比"></a>静态变量与局部变量的对比</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">变量的分类：</span><br><span class="line"><span class="number">1</span>、按照数据类型分：① 基本数据类型  ② 引用数据类型</span><br><span class="line"><span class="number">2</span>、按照在类中声明的位置分：</span><br><span class="line">  <span class="number">2</span>-<span class="number">1</span>、成员变量：在使用前，都经历过默认初始化赋值</span><br><span class="line">       <span class="number">2</span>-<span class="number">1</span>-<span class="number">1</span>、类变量: linking的prepare阶段：给类变量默认赋值</span><br><span class="line">              ---&gt; initial阶段：给类变量显式赋值即静态代码块赋值</span><br><span class="line">       <span class="number">2</span>-<span class="number">1</span>-<span class="number">2</span>、实例变量：随着对象的创建，会在堆空间中分配实例变量空间，并进行默认赋值</span><br><span class="line">  <span class="number">2</span>-<span class="number">2</span>、局部变量：在使用前，必须要进行显式赋值的！否则，编译不通过。</span><br></pre></td></tr></table></figure>



<ol>
<li> 参数表分配完毕之后，再根据方法体内定义的变量的顺序和作用域分配。</li>
<li> 我们知道成员变量有两次初始化的机会<strong>，</strong>第一次是在“准备阶段”，执行系统初始化，对类变量设置零值，另一次则是在“初始化”阶段，赋予程序员在代码中定义的初始值。</li>
<li> 和类变量初始化不同的是，<strong>局部变量表不存在系统初始化的过程</strong>，这意味着一旦定义了局部变量则必须人为的初始化，否则无法使用。</li>
</ol>
<h3 id="补充说明"><a href="#补充说明" class="headerlink" title="补充说明"></a>补充说明</h3><ol>
<li>在栈帧中，与性能调优关系最为密切的部分就是前面提到的局部变量表。在方法执行时，虚拟机使用局部变量表完成方法的传递。</li>
<li>局部变量表中的变量也是重要的垃圾回收根节点，只要被局部变量表中直接或间接引用的对象都不会被回收。</li>
</ol>
<h2 id="操作数栈"><a href="#操作数栈" class="headerlink" title="操作数栈"></a>操作数栈</h2><h3 id="操作数栈的特点"><a href="#操作数栈的特点" class="headerlink" title="操作数栈的特点"></a>操作数栈的特点</h3><ol>
<li>每一个独立的栈帧除了包含局部变量表以外，还包含一个后进先出（Last - In - First -Out）的 操作数栈，也可以称之为<strong>表达式栈</strong>（Expression Stack）</li>
<li> 操作数栈，在方法执行过程中，<strong>根据字节码指令，往栈中写入数据或提取数据</strong>，即入栈（push）和 出栈（pop）</li>
</ol>
<ul>
<li>某些字节码指令将值压入操作数栈，其余的字节码指令将操作数取出栈。使用它们后再把结果压入栈，</li>
<li>比如：执行复制、交换、求和等操作</li>
</ul>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0018.png">





<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0019.png">



<h3 id="操作数栈的作用"><a href="#操作数栈的作用" class="headerlink" title="操作数栈的作用"></a>操作数栈的作用</h3><ol>
<li>操作数栈，<strong>主要用于保存计算过程的中间结果，同时作为计算过程中变量临时的存储空间</strong>。</li>
<li>操作数栈就是JVM执行引擎的一个工作区，当一个方法刚开始执行的时候，一个新的栈帧也会随之被创建出来，这时方法的操作数栈是空的。</li>
<li>每一个操作数栈都会拥有一个明确的栈深度用于存储数值，其所需的最大深度在编译期就定义好了，保存在方法的Code属性中，为<strong>maxstack</strong>的值。</li>
<li>栈中的任何一个元素都是可以任意的Java数据类型<ul>
<li>  32bit的类型占用一个栈单位深度</li>
<li>  64bit的类型占用两个栈单位深度</li>
</ul>
</li>
<li>操作数栈并非采用访问索引的方式来进行数据访问的，而是只能通过标准的入栈和出栈操作来完成一次数据访问。<strong>只不过操作数栈是用数组这个结构来实现的而已</strong></li>
<li>如果被调用的方法带有返回值的话，其返回值将会被压入当前栈帧的操作数栈中，并更新PC寄存器中下一条需要执行的字节码指令。</li>
<li>操作数栈中元素的数据类型必须与字节码指令的序列严格匹配，这由编译器在编译器期间进行验证，同时在类加载过程中的类检验阶段的数据流分析阶段要再次验证。</li>
<li>另外，<strong>我们说Java虚拟机的解释引擎是基于栈的执行引擎，其中的栈指的就是操作数栈</strong>。</li>
</ol>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0020.jpg">

<p>局部变量表就相当于食材</p>
<p>操作数栈就相当于做法步骤</p>
<h2 id="操作数栈代码追踪"><a href="#操作数栈代码追踪" class="headerlink" title="操作数栈代码追踪"></a>操作数栈代码追踪</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testAddOperation</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       <span class="comment">//byte、short、char、boolean：都以int型来保存</span></span><br><span class="line">       <span class="keyword">byte</span> i = <span class="number">15</span>;</span><br><span class="line">       <span class="keyword">int</span> j = <span class="number">8</span>;</span><br><span class="line">       <span class="keyword">int</span> k = i + j;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// int m = 800;</span></span><br><span class="line"></span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<p>对应字节码指令</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="number">0</span> bipush <span class="number">15</span></span><br><span class="line"> <span class="number">2</span> istore_1</span><br><span class="line"> <span class="number">3</span> bipush <span class="number">8</span></span><br><span class="line"> <span class="number">5</span> istore_2</span><br><span class="line"> <span class="number">6</span> iload_1</span><br><span class="line"> <span class="number">7</span> iload_2</span><br><span class="line"> <span class="number">8</span> iadd</span><br><span class="line"> <span class="number">9</span> istore_3</span><br><span class="line"><span class="number">10</span> <span class="keyword">return</span></span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0021.png">

<h3 id="一步一步看流程"><a href="#一步一步看流程" class="headerlink" title="一步一步看流程"></a>一步一步看流程</h3><p>1、首先执行第一条语句，PC寄存器指向的是0，也就是指令地址为0，然后使用bipush让操作数15入操作数栈。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0022.png">

<p>2、执行完后，PC寄存器往下移，指向下一行代码，下一行代码就是将操作数栈的元素存储到局部变量表1的位置（istore_1），我们可以看到局部变量表的已经增加了一个元素。并且操作数栈为空了</p>
<ul>
<li>  解释为什么局部变量表索引从 1 开始，因为该方法为实例方法，局部变量表索引为 0 的位置存放的是 this</li>
</ul>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0023.png">

<p>3、然后PC下移，指向的是下一行。让操作数8也入栈，同时执行store操作，存入局部变量表中</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0024.png">



<p>4、然后从局部变量表中，依次将数据放在操作数栈中，等待执行 add 操作</p>
<p>iload_1：取出局部变量表中索引为1的数据入操作数栈</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0025.png">

<p>5、然后将操作数栈中的两个元素执行相加操作，并存储在局部变量表3的位置</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0026.png">



<h3 id="小问题"><a href="#小问题" class="headerlink" title="小问题"></a>小问题</h3><p><strong>关于类型转换的说明</strong></p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0027.png">



<ul>
<li>  因为 8 可以存放在 byte 类型中，所以压入操作数栈的类型为 byte ，而不是 int ，所以执行的字节码指令为 bipush 8</li>
<li>  但是存储在局部变量的时候，会转成 int 类型的变量：istore_4</li>
</ul>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0028.png">

<ul>
<li>m改成800之后，byte存储不了，就成了short型，sipush 800</li>
</ul>
<p><strong>如果被调用的方法带有返回值，返回值入操作数栈</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getSum</span><span class="params">()</span></span>&#123;</span><br><span class="line">      <span class="keyword">int</span> m = <span class="number">10</span>;</span><br><span class="line">      <span class="keyword">int</span> n = <span class="number">20</span>;</span><br><span class="line">      <span class="keyword">int</span> k = m + n;</span><br><span class="line">      <span class="keyword">return</span> k;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testGetSum</span><span class="params">()</span></span>&#123;</span><br><span class="line">      <span class="comment">//获取上一个栈桢返回的结果，并保存在操作数栈中</span></span><br><span class="line">      <span class="keyword">int</span> i = getSum();</span><br><span class="line">      <span class="keyword">int</span> j = <span class="number">10</span>;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>



<p>getSum() 方法字节码指令：最后带着个 ireturn</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0029.png">

<p>testGetSum() 方法字节码指令：一上来就加载 getSum() 方法的返回值()</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0030.png">





<h2 id="栈顶缓存技术"><a href="#栈顶缓存技术" class="headerlink" title="栈顶缓存技术"></a>栈顶缓存技术</h2><p><strong>栈顶缓存技术：Top Of Stack Cashing</strong></p>
<ol>
<li>前面提过，基于栈式架构的虚拟机所使用的零地址指令更加紧凑，但完成一项操作的时候必然需要使用更多的入栈和出栈指令，这同时也就意味着将需要更多的指令分派（instruction dispatch）次数（也就是你会发现指令很多）和导致内存读/写次数多，效率不高。</li>
<li>由于操作数是存储在内存中的，因此频繁地执行内存读/写操作必然会影响执行速度。为了解决这个问题，HotSpot JVM的设计者们提出了栈顶缓存（Tos，Top-of-Stack Cashing）技术，<strong>将栈顶元素全部缓存在物理CPU的寄存器中，以此降低对内存的读/写次数，提升执行引擎的执行效率。</strong></li>
<li>寄存器的主要优点：指令更少，执行速度快，但是指令集（也就是指令种类）很多</li>
</ol>
<h2 id="动态链接"><a href="#动态链接" class="headerlink" title="动态链接"></a>动态链接</h2><p><strong>动态链接（或指向运行时常量池的方法引用）</strong></p>
<ol>
<li>每一个栈帧内部都包含<strong>一个指向运行时常量池中该栈帧所属方法的引用</strong>。包含这个引用的目的就是<strong>为了支持当前方法的代码能够实现动态链接</strong>（Dynamic Linking），比如：invokedynamic指令</li>
<li>在Java源文件被编译到字节码文件中时，所有的变量和方法引用都作为符号引用（Symbolic Reference）保存在class文件的常量池里。比如：描述一个方法调用了另外的其他方法时，就是通过常量池中指向方法的符号引用来表示的，那么<strong>动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用</strong></li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DynamicLinkingTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> num = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">methodA</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;methodA()....&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">methodB</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;methodB()....&quot;</span>);</span><br><span class="line"></span><br><span class="line">        methodA();</span><br><span class="line"></span><br><span class="line">        num++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对应字节码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Classfile /F:/IDEAWorkSpaceSourceCode/JVMDemo/out/production/chapter05/com/atguigu/java1/DynamicLinkingTest.class</span><br><span class="line">  Last modified <span class="number">2020</span>-<span class="number">11</span>-<span class="number">10</span>; size <span class="number">712</span> bytes</span><br><span class="line">  MD5 checksum e56913c945f897c7ee6c0a608629bca8</span><br><span class="line">  Compiled from <span class="string">&quot;DynamicLinkingTest.java&quot;</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">atguigu</span>.<span class="title">java1</span>.<span class="title">DynamicLinkingTest</span></span></span><br><span class="line">  minor version: 0</span><br><span class="line">  major version: <span class="number">52</span></span><br><span class="line">  flags: ACC_PUBLIC, ACC_SUPER</span><br><span class="line">Constant pool:</span><br><span class="line">   #1 = Methodref          #9.#23         // java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">   #2 = Fieldref           #8.#24         // com/atguigu/java1/DynamicLinkingTest.num:I</span><br><span class="line">   #3 = Fieldref           #25.#26        // java/lang/System.out:Ljava/io/PrintStream;</span><br><span class="line">   #4 = String             #27            // methodA()....</span><br><span class="line">   #5 = Methodref          #28.#29        // java/io/PrintStream.println:(Ljava/lang/String;)V</span><br><span class="line">   #6 = String             #30            // methodB()....</span><br><span class="line">   #7 = Methodref          #8.#31         // com/atguigu/java1/DynamicLinkingTest.methodA:()V</span><br><span class="line">   #8 = Class              #32            // com/atguigu/java1/DynamicLinkingTest</span><br><span class="line">   #9 = Class              #33            // java/lang/Object</span><br><span class="line">  #10 = Utf8               num</span><br><span class="line">  #11 = Utf8               I</span><br><span class="line">  #12 = Utf8               &lt;init&gt;</span><br><span class="line">  #13 = Utf8               ()V</span><br><span class="line">  #14 = Utf8               Code</span><br><span class="line">  #15 = Utf8               LineNumberTable</span><br><span class="line">  #16 = Utf8               LocalVariableTable</span><br><span class="line">  #17 = Utf8               this</span><br><span class="line">  #18 = Utf8               Lcom/atguigu/java1/DynamicLinkingTest;</span><br><span class="line">  #19 = Utf8               methodA</span><br><span class="line">  #20 = Utf8               methodB</span><br><span class="line">  #21 = Utf8               SourceFile</span><br><span class="line">  #22 = Utf8               DynamicLinkingTest.java</span><br><span class="line">  #23 = NameAndType        #12:#13        // &quot;&lt;init&gt;&quot;:()V</span><br><span class="line">  #24 = NameAndType        #10:#11        // num:I</span><br><span class="line">  #25 = Class              #34            // java/lang/System</span><br><span class="line">  #26 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;</span><br><span class="line">  #27 = Utf8               methodA()....</span><br><span class="line">  #28 = Class              #37            // java/io/PrintStream</span><br><span class="line">  #29 = NameAndType        #38:#39        // println:(Ljava/lang/String;)V</span><br><span class="line">  #30 = Utf8               methodB()....</span><br><span class="line">  #31 = NameAndType        #19:#13        // methodA:()V</span><br><span class="line">  #32 = Utf8               com/atguigu/java1/DynamicLinkingTest</span><br><span class="line">  #33 = Utf8               java/lang/Object</span><br><span class="line">  #34 = Utf8               java/lang/System</span><br><span class="line">  #35 = Utf8               out</span><br><span class="line">  #36 = Utf8               Ljava/io/PrintStream;</span><br><span class="line">  #37 = Utf8               java/io/PrintStream</span><br><span class="line">  #38 = Utf8               println</span><br><span class="line">  #39 = Utf8               (Ljava/lang/String;)V</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">int</span> num;</span><br><span class="line">    descriptor: I</span><br><span class="line">    flags:</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> com.atguigu.java1.DynamicLinkingTest();</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=<span class="number">2</span>, locals=<span class="number">1</span>, args_size=<span class="number">1</span></span><br><span class="line">         <span class="number">0</span>: aload_0</span><br><span class="line">         1: invokespecial #1                  // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">         <span class="number">4</span>: aload_0</span><br><span class="line">         <span class="number">5</span>: bipush        <span class="number">10</span></span><br><span class="line">         7: putfield      #2                  // Field num:I</span><br><span class="line">        <span class="number">10</span>: <span class="keyword">return</span></span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line <span class="number">7</span>: <span class="number">0</span></span><br><span class="line">        line <span class="number">9</span>: <span class="number">4</span></span><br><span class="line">      LocalVariableTable:</span><br><span class="line">        Start  Length  Slot  Name   Signature</span><br><span class="line">            <span class="number">0</span>      <span class="number">11</span>     <span class="number">0</span>  <span class="keyword">this</span>   Lcom/atguigu/java1/DynamicLinkingTest;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">methodA</span><span class="params">()</span></span>;</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=<span class="number">2</span>, locals=<span class="number">1</span>, args_size=<span class="number">1</span></span><br><span class="line">         0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;</span><br><span class="line">         3: ldc           #4                  // String methodA()....</span><br><span class="line">         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V</span><br><span class="line">         <span class="number">8</span>: <span class="keyword">return</span></span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line <span class="number">12</span>: <span class="number">0</span></span><br><span class="line">        line <span class="number">13</span>: <span class="number">8</span></span><br><span class="line">      LocalVariableTable:</span><br><span class="line">        Start  Length  Slot  Name   Signature</span><br><span class="line">            <span class="number">0</span>       <span class="number">9</span>     <span class="number">0</span>  <span class="keyword">this</span>   Lcom/atguigu/java1/DynamicLinkingTest;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">methodB</span><span class="params">()</span></span>;</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=<span class="number">3</span>, locals=<span class="number">1</span>, args_size=<span class="number">1</span></span><br><span class="line">         0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;</span><br><span class="line">         3: ldc           #6                  // String methodB()....</span><br><span class="line">         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V</span><br><span class="line">         <span class="number">8</span>: aload_0</span><br><span class="line">         9: invokevirtual #7                  // Method methodA:()V</span><br><span class="line">        <span class="number">12</span>: aload_0</span><br><span class="line">        <span class="number">13</span>: dup</span><br><span class="line">        14: getfield      #2                  // Field num:I</span><br><span class="line">        <span class="number">17</span>: iconst_1</span><br><span class="line">        <span class="number">18</span>: iadd</span><br><span class="line">        19: putfield      #2                  // Field num:I</span><br><span class="line">        <span class="number">22</span>: <span class="keyword">return</span></span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line <span class="number">16</span>: <span class="number">0</span></span><br><span class="line">        line <span class="number">18</span>: <span class="number">8</span></span><br><span class="line">        line <span class="number">20</span>: <span class="number">12</span></span><br><span class="line">        line <span class="number">21</span>: <span class="number">22</span></span><br><span class="line">      LocalVariableTable:</span><br><span class="line">        Start  Length  Slot  Name   Signature</span><br><span class="line">            <span class="number">0</span>      <span class="number">23</span>     <span class="number">0</span>  <span class="keyword">this</span>   Lcom/atguigu/java1/DynamicLinkingTest;</span><br><span class="line">&#125;</span><br><span class="line">SourceFile: <span class="string">&quot;DynamicLinkingTest.java&quot;</span></span><br></pre></td></tr></table></figure>



<p>1、在字节码指令中，methodB() 方法中通过 invokevirtual #7 指令调用了方法 A ，那么 #7 是个啥呢？</p>
<p>2、往上面翻，找到常量池的定义：<code>#7 = Methodref #8.#31</code></p>
<ul>
<li>先找 #8 ：<ul>
<li>  <code>#8 = Class #32</code> ：去找 #32</li>
<li>  <code>#32 = Utf8 com/atguigu/java1/DynamicLinkingTest</code></li>
<li>  结论：通过 #8 我们找到了 <code>DynamicLinkingTest</code> 这个类</li>
</ul>
</li>
<li>再来找 #31：<ul>
<li>  <code>#31 = NameAndType #19:#13</code> ：去找 #19 和 #13</li>
<li>  <code>#19 = Utf8 methodA</code> ：方法名为 methodA</li>
<li>  <code>#13 = Utf8 ()V</code> ：方法没有形参，返回值为 void</li>
</ul>
</li>
</ul>
<p>3、结论：通过 #7 我们就能找到需要调用的 methodA() 方法，并进行调用</p>
<p>4、在上面，其实还有很多符号引用，比如 Object、System、PrintStream 等等</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0031.png">



<p><strong>为什么要用常量池呢？</strong></p>
<ol>
<li>因为在不同的方法，都可能调用常量或者方法，所以只需要存储一份即可，然后记录其引用即可，节省了空间。</li>
<li>常量池的作用：就是为了提供一些符号和常量，便于指令的识别</li>
</ol>
<h2 id="方法的调用"><a href="#方法的调用" class="headerlink" title="方法的调用"></a>方法的调用</h2><h3 id="静态链接与动态链接"><a href="#静态链接与动态链接" class="headerlink" title="静态链接与动态链接"></a>静态链接与动态链接</h3><p>在JVM中，将符号引用转换为调用方法的直接引用与方法的绑定机制相关</p>
<ul>
<li><strong>静态链接</strong>：</li>
</ul>
<p>当一个字节码文件被装载进JVM内部时，如果被调用的目标方法在编译期确定，且运行期保持不变时，这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接</p>
<ul>
<li><strong>动态链接</strong>：</li>
</ul>
<p>如果被调用的方法在编译期无法被确定下来，也就是说，只能够在程序运行期将调用的方法的符号转换为直接引用，由于这种引用转换过程具备动态性，因此也被称之为动态链接。</p>
<h3 id="早期绑定与晚期绑定"><a href="#早期绑定与晚期绑定" class="headerlink" title="早期绑定与晚期绑定"></a>早期绑定与晚期绑定</h3><blockquote>
<p>静态链接与动态链接针对的是方法。早期绑定和晚期绑定范围更广。早期绑定涵盖了静态链接，晚期绑定涵盖了动态链接。</p>
</blockquote>
<p>静态链接和动态链接对应的方法的绑定机制为：早期绑定（Early Binding）和晚期绑定（Late Binding）。<strong>绑定是一个字段、方法或者类在符号引用被替换为直接引用的过程</strong>，这仅仅发生一次。</p>
<ul>
<li><strong>早期绑定</strong></li>
</ul>
<p>早期绑定就是指被调用的目标方法如果在编译期可知，且运行期保持不变时，即可将这个方法与所属的类型进行绑定，这样一来，由于明确了被调用的目标方法究竟是哪一个，因此也就<strong>可以使用静态链接的方式将符号引用转换为直接引用</strong>。</p>
<ul>
<li><strong>晚期绑定</strong></li>
</ul>
<p>如果被调用的方法在编译期无法被确定下来，<strong>只能够在程序运行期根据实际的类型绑定相关的方法</strong>，这种绑定方式也就被称之为晚期绑定。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Animal</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;动物进食&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Huntable</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">hunt</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dog</span> <span class="keyword">extends</span> <span class="title">Animal</span> <span class="keyword">implements</span> <span class="title">Huntable</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;狗吃骨头&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">hunt</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;捕食耗子，多管闲事&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Cat</span> <span class="keyword">extends</span> <span class="title">Animal</span> <span class="keyword">implements</span> <span class="title">Huntable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Cat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>();<span class="comment">//表现为：早期绑定</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Cat</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>();<span class="comment">//表现为：早期绑定</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.eat();<span class="comment">//表现为：早期绑定</span></span><br><span class="line">        System.out.println(<span class="string">&quot;猫吃鱼&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">hunt</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;捕食耗子，天经地义&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AnimalTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showAnimal</span><span class="params">(Animal animal)</span> </span>&#123;</span><br><span class="line">        animal.eat();<span class="comment">//表现为：晚期绑定</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showHunt</span><span class="params">(Huntable h)</span> </span>&#123;</span><br><span class="line">        h.hunt();<span class="comment">//表现为：晚期绑定</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>部分字节码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="keyword">public</span> com.atguigu.java2.AnimalTest();</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=<span class="number">1</span>, locals=<span class="number">1</span>, args_size=<span class="number">1</span></span><br><span class="line">         <span class="number">0</span>: aload_0</span><br><span class="line">         1: invokespecial #1                  // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">         <span class="number">4</span>: <span class="keyword">return</span></span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line <span class="number">54</span>: <span class="number">0</span></span><br><span class="line">      LocalVariableTable:</span><br><span class="line">        Start  Length  Slot  Name   Signature</span><br><span class="line">            <span class="number">0</span>       <span class="number">5</span>     <span class="number">0</span>  <span class="keyword">this</span>   Lcom/atguigu/java2/AnimalTest;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showAnimal</span><span class="params">(com.atguigu.java2.Animal)</span></span>;</span><br><span class="line">    descriptor: (Lcom/atguigu/java2/Animal;)V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=<span class="number">1</span>, locals=<span class="number">2</span>, args_size=<span class="number">2</span></span><br><span class="line">         <span class="number">0</span>: aload_1</span><br><span class="line">         1: invokevirtual #2                  // Method com/atguigu/java2/Animal.eat:()V</span><br><span class="line">         <span class="number">4</span>: <span class="keyword">return</span></span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line <span class="number">56</span>: <span class="number">0</span></span><br><span class="line">        line <span class="number">57</span>: <span class="number">4</span></span><br><span class="line">      LocalVariableTable:</span><br><span class="line">        Start  Length  Slot  Name   Signature</span><br><span class="line">            <span class="number">0</span>       <span class="number">5</span>     <span class="number">0</span>  <span class="keyword">this</span>   Lcom/atguigu/java2/AnimalTest;</span><br><span class="line">            <span class="number">0</span>       <span class="number">5</span>     <span class="number">1</span> animal   Lcom/atguigu/java2/Animal;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showHunt</span><span class="params">(com.atguigu.java2.Huntable)</span></span>;</span><br><span class="line">    descriptor: (Lcom/atguigu/java2/Huntable;)V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=<span class="number">1</span>, locals=<span class="number">2</span>, args_size=<span class="number">2</span></span><br><span class="line">         <span class="number">0</span>: aload_1</span><br><span class="line">         1: invokeinterface #3,  1            // InterfaceMethod com/atguigu/java2/Huntable.hunt:()V</span><br><span class="line">         <span class="number">6</span>: <span class="keyword">return</span></span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line <span class="number">60</span>: <span class="number">0</span></span><br><span class="line">        line <span class="number">61</span>: <span class="number">6</span></span><br><span class="line">      LocalVariableTable:</span><br><span class="line">        Start  Length  Slot  Name   Signature</span><br><span class="line">            <span class="number">0</span>       <span class="number">7</span>     <span class="number">0</span>  <span class="keyword">this</span>   Lcom/atguigu/java2/AnimalTest;</span><br><span class="line">            <span class="number">0</span>       <span class="number">7</span>     <span class="number">1</span>     h   Lcom/atguigu/java2/Huntable;</span><br><span class="line">&#125;</span><br><span class="line">SourceFile: <span class="string">&quot;AnimalTest.java&quot;</span></span><br></pre></td></tr></table></figure>



<p>invokevirtual 体现为晚期绑定</p>
<p>invokeinterface 也体现为晚期绑定</p>
<p>invokespecial 体现为早期绑定</p>
<h3 id="多态与绑定"><a href="#多态与绑定" class="headerlink" title="多态与绑定"></a>多态与绑定</h3><ol>
<li>随着高级语言的横空出世，类似于Java一样的基于面向对象的编程语言如今越来越多，尽管这类编程语言在语法风格上存在一定的差别，但是它们彼此之间始终保持着一个共性，那就是都支持封装、继承和多态等面向对象特性，既然这一类的编程语言具备多态特性，那么自然也就具备早期绑定和晚期绑定两种绑定方式。</li>
<li>Java中任何一个普通的方法其实都具备虚函数的特征，它们相当于C++语言中的虚函数（C++中则需要使用关键字virtual来显式定义）。如果在Java程序中不希望某个方法拥有虚函数的特征时，则可以使用关键字final来标记这个方法。</li>
</ol>
<h4 id="虚方法与非虚方法"><a href="#虚方法与非虚方法" class="headerlink" title="虚方法与非虚方法"></a>虚方法与非虚方法</h4><p><strong>虚方法与非虚方法的区别</strong></p>
<ol>
<li> 如果方法在编译期就确定了具体的调用版本，这个版本在运行时是不可变的。这样的方法称为非虚方法。</li>
<li> 静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法。</li>
<li> 其他方法称为虚方法。</li>
</ol>
<p><strong>子类对象的多态的使用前提：</strong></p>
<ol>
<li> 类的继承关系</li>
<li> 方法的重写</li>
</ol>
<p><strong>虚拟机中调用方法的指令</strong></p>
<ul>
<li><strong>普通指令：</strong></li>
</ul>
<ol>
<li> invokestatic：调用静态方法，解析阶段确定唯一方法版本</li>
<li> invokespecial：调用<code>&lt;init&gt;</code>方法、私有及父类方法，解析阶段确定唯一方法版本</li>
<li> invokevirtual：调用所有虚方法</li>
<li> invokeinterface：调用接口方法</li>
</ol>
<ul>
<li><strong>动态调用指令</strong></li>
</ul>
<p>invokedynamic：动态解析出需要调用的方法，然后执行</p>
<p>前四条指令固化在虚拟机内部，方法的调用执行不可人为干预。而invokedynamic指令则支持由用户确定方法版本。其中invokestatic指令和invokespecial指令调用的方法称为非虚方法，其余的（final修饰的除外）称为虚方法。</p>
<h4 id="举例-2"><a href="#举例-2" class="headerlink" title="举例"></a>举例</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Father</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Father</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;father的构造器&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">showStatic</span><span class="params">(String str)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;father &quot;</span> + str);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">showFinal</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;father show final&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showCommon</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;father 普通方法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Son</span> <span class="keyword">extends</span> <span class="title">Father</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Son</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//invokespecial</span></span><br><span class="line">        <span class="keyword">super</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Son</span><span class="params">(<span class="keyword">int</span> age)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//invokespecial</span></span><br><span class="line">        <span class="keyword">this</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//不是重写的父类的静态方法，因为静态方法不能被重写！</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">showStatic</span><span class="params">(String str)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;son &quot;</span> + str);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">showPrivate</span><span class="params">(String str)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;son private&quot;</span> + str);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">show</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//invokestatic</span></span><br><span class="line">        showStatic(<span class="string">&quot;atguigu.com&quot;</span>);</span><br><span class="line">        <span class="comment">//invokestatic</span></span><br><span class="line">        <span class="keyword">super</span>.showStatic(<span class="string">&quot;good!&quot;</span>);</span><br><span class="line">        <span class="comment">//invokespecial</span></span><br><span class="line">        showPrivate(<span class="string">&quot;hello!&quot;</span>);</span><br><span class="line">        <span class="comment">//invokespecial</span></span><br><span class="line">        <span class="keyword">super</span>.showCommon();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//invokevirtual</span></span><br><span class="line">        showFinal();<span class="comment">//因为此方法声明有final，不能被子类重写，所以也认为此方法是非虚方法。</span></span><br><span class="line">        <span class="comment">//虚方法如下：</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        invokevirtual  你没有显示的加super.，编译器认为你可能调用子类的showCommon(即使son子类没有重写，也		  会认为)，所以编译期间确定不下来，就是虚方法。</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        showCommon();</span><br><span class="line">        info();</span><br><span class="line"></span><br><span class="line">        MethodInterface in = <span class="keyword">null</span>;</span><br><span class="line">        <span class="comment">//invokeinterface</span></span><br><span class="line">        in.methodA();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">info</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">display</span><span class="params">(Father f)</span> </span>&#123;</span><br><span class="line">        f.showCommon();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Son so = <span class="keyword">new</span> Son();</span><br><span class="line">        so.show();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">MethodInterface</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">methodA</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Son 类中 show() 方法的字节码指令如下</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0032.png">



<h4 id="关于-invokedynamic-指令"><a href="#关于-invokedynamic-指令" class="headerlink" title="关于 invokedynamic 指令"></a>关于 invokedynamic 指令</h4><ol>
<li>JVM字节码指令集一直比较稳定，一直到Java7中才增加了一个invokedynamic指令，这是Java为了实现【动态类型语言】支持而做的一种改进。</li>
<li>但是在Java7中并没有提供直接生成invokedynamic指令的方法，需要借助ASM这种底层字节码工具来产生invokedynamic指令。直到Java8的Lambda表达式的出现，invokedynamic指令的生成，在Java中才有了直接的生成方式。</li>
<li>Java7中增加的动态语言类型支持的本质是对Java虚拟机规范的修改，而不是对Java语言规则的修改，这一块相对来讲比较复杂，增加了虚拟机中的方法调用，最直接的受益者就是运行在Java平台的动态语言的编译器。</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Func</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">func</span><span class="params">(String str)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Lambda</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">lambda</span><span class="params">(Func func)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Lambda lambda = <span class="keyword">new</span> Lambda();</span><br><span class="line"></span><br><span class="line">        Func func = s -&gt; &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        lambda.lambda(func);</span><br><span class="line"></span><br><span class="line">        lambda.lambda(s -&gt; &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0033.png">





<h3 id="动态语言和静态语言"><a href="#动态语言和静态语言" class="headerlink" title="动态语言和静态语言"></a>动态语言和静态语言</h3><ol>
<li>动态类型语言和静态类型语言两者的区别就在于<strong>对类型的检查是在编译期还是在运行期</strong>，满足前者就是静态类型语言，反之是动态类型语言。</li>
<li>说的再直白一点就是，静态类型语言是判断变量自身的类型信息；动态类型语言是判断变量值的类型信息，变量没有类型信息，变量值才有类型信息，这是动态语言的一个重要特征。</li>
</ol>
<p>Java：String info = “mogu blog”;             (Java是静态类型语言的，会先编译就进行类型检查)<br>    JS：var name = “shkstart”;    var name = 10;    （运行时才进行检查）</p>
<pre><code>Python: info = 130.5 (运行时才检查)
</code></pre>
<h3 id="Java语言中方法重写的本质"><a href="#Java语言中方法重写的本质" class="headerlink" title="Java语言中方法重写的本质"></a>Java语言中方法重写的本质</h3><ol>
<li> 找到操作数栈顶的第一个元素所执行的对象的实际类型，记作C。</li>
<li>如果在类型C中找到与常量中的描述符合简单名称都相符的方法，则进行访问权限校验。<ul>
<li>  如果通过则返回这个方法的直接引用，查找过程结束</li>
<li>  如果不通过，则返回java.lang.IllegalAccessError 异常</li>
</ul>
</li>
<li> 否则，按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。</li>
<li> 如果始终没有找到合适的方法，则抛出java.lang.AbstractMethodError异常。</li>
</ol>
<blockquote>
<p>上面这个过程称为<strong>动态分派</strong></p>
</blockquote>
<p><strong>IllegalAccessError介绍</strong></p>
<ol>
<li> 程序试图访问或修改一个属性或调用一个方法，这个属性或方法，你没有权限访问。一般的，这个会引起编译器异常。这个错误如果发生在运行时，就说明一个类发生了不兼容的改变。</li>
<li> 比如，你把应该有的jar包放从工程中拿走了，或者Maven中存在jar包冲突</li>
</ol>
<h3 id="虚方法表"><a href="#虚方法表" class="headerlink" title="虚方法表"></a>虚方法表</h3><ol>
<li>在面向对象的编程中，会很频繁的使用到<strong>动态分派</strong>，如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话就可能影响到执行效率。因此，为了提高性能，<strong>JVM采用在类的方法区建立一个虚方法表（virtual method table）来实现</strong>，非虚方法不会出现在表中。使用索引表来代替查找。【上面动态分派的过程，我们可以看到如果子类找不到，还要从下往上找其父类，非常耗时】</li>
<li>每个类中都有一个虚方法表，表中存放着各个方法的实际入口。</li>
<li>虚方法表是什么时候被创建的呢？虚方法表会在类加载的链接阶段被创建并开始初始化，类的变量初始值准备完成之后，JVM会把该类的虚方法表也初始化完毕。</li>
</ol>
<p><strong>例子1</strong></p>
<p>如图所示：如果类中重写了方法，那么调用的时候，就会直接在该类的虚方法表中查找</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0034.png">

<p>1、比如说son在调用toString的时候，Son没有重写过，Son的父类Father也没有重写过，那就直接调用Object类的toString。那么就直接在虚方法表里指明toString直接指向Object类。</p>
<p>2、下次Son对象再调用toString就直接去找Object，不用先找Son–&gt;再找Father–&gt;最后才到Object的这样的一个过程。</p>
<p><strong>例子2</strong></p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0035.png">



<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0036.jpg">

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0037.jpg">



<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0038.jpg">



<h2 id="方法返回地址"><a href="#方法返回地址" class="headerlink" title="方法返回地址"></a>方法返回地址</h2><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0039.png">

<blockquote>
<p>在一些帖子里，方法返回地址、动态链接、一些附加信息  也叫做帧数据区</p>
</blockquote>
<ol>
<li>存放调用该方法的pc寄存器的值。一个方法的结束，有两种方式：<ul>
<li>  正常执行完成</li>
<li>  出现未处理的异常，非正常退出</li>
</ul>
</li>
<li>无论通过哪种方式退出，在方法退出后都返回到该方法被调用的位置。方法正常退出时，<strong>调用者的pc计数器的值作为返回地址，即调用该方法的指令的下一条指令的地址</strong>。而通过异常退出的，返回地址是要通过异常表来确定，栈帧中一般不会保存这部分信息。</li>
<li>本质上，方法的退出就是当前栈帧出栈的过程。此时，需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等，让调用者方法继续执行下去。</li>
<li>正常完成出口和异常完成出口的区别在于：通过异常完成出口退出的不会给他的上层调用者产生任何的返回值。</li>
</ol>
<p><strong>方法退出的两种方式</strong></p>
<p>当一个方法开始执行后，只有两种方式可以退出这个方法，</p>
<p><strong>正常退出：</strong></p>
<ol>
<li><p> 执行引擎遇到任意一个方法返回的字节码指令（return），会有返回值传递给上层的方法调用者，简称<strong>正常完成出口</strong>；</p>
</li>
<li><p> 一个方法在正常调用完成之后，究竟需要使用哪一个返回指令，还需要根据方法返回值的实际数据类型而定。</p>
</li>
<li><p>在字节码指令中，返回指令包含：</p>
<ul>
<li><p>ireturn：当返回值是boolean，byte，char，short和int类型时使用</p>
</li>
<li><p>lreturn：Long类型</p>
</li>
<li><p>freturn：Float类型</p>
</li>
<li><p>dreturn：Double类型</p>
</li>
<li><p>areturn：引用类型</p>
</li>
<li><p>return：返回值类型为void的方法、实例初始化方法、类和接口的初始化方法</p>
</li>
</ul>
</li>
</ol>
<p><strong>异常退出：</strong></p>
<ol>
<li>在方法执行过程中遇到异常（Exception），并且这个异常没有在方法内进行处理，也就是只要在本方法的异常表中没有搜索到匹配的异常处理器，就会导致方法退出，简称<strong>异常完成出口</strong>。</li>
<li>方法执行过程中，抛出异常时的异常处理，存储在一个异常处理表，方便在发生异常的时候找到处理异常的代码</li>
</ol>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0040.png">



<p>异常处理表：</p>
<ul>
<li>  反编译字节码文件，可得到 Exception table</li>
<li>  from ：字节码指令起始地址</li>
<li>  to ：字节码指令结束地址</li>
<li>  target ：出现异常跳转至地址为 11 的指令执行</li>
<li>  type ：捕获异常的类型</li>
</ul>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/JVM/chapter_004/0041.png">



<h2 id="一些附加信息"><a href="#一些附加信息" class="headerlink" title="一些附加信息"></a>一些附加信息</h2><p>栈帧中还允许携带与Java虚拟机实现相关的一些附加信息。例如：对程序调试提供支持的信息。</p>
<h2 id="栈相关面试题"><a href="#栈相关面试题" class="headerlink" title="栈相关面试题"></a>栈相关面试题</h2><h3 id="举例栈溢出的情况？"><a href="#举例栈溢出的情况？" class="headerlink" title="举例栈溢出的情况？"></a>举例栈溢出的情况？</h3><p>SOF（StackOverflowError），栈大小分为固定的，和动态变化。如果是固定的就可能出现StackOverflowError。如果是动态变化的，内存不足时就可能出现OOM</p>
<h3 id="调整栈大小，就能保证不出现溢出么？"><a href="#调整栈大小，就能保证不出现溢出么？" class="headerlink" title="调整栈大小，就能保证不出现溢出么？"></a>调整栈大小，就能保证不出现溢出么？</h3><p>不能保证不溢出，只能保证SOF出现的几率小</p>
<h3 id="分配的栈内存越大越好么？"><a href="#分配的栈内存越大越好么？" class="headerlink" title="分配的栈内存越大越好么？"></a>分配的栈内存越大越好么？</h3><p>不是，一定时间内降低了OOM概率，但是会挤占其它的线程空间，因为整个虚拟机的内存空间是有限的</p>
<h3 id="垃圾回收是否涉及到虚拟机栈？"><a href="#垃圾回收是否涉及到虚拟机栈？" class="headerlink" title="垃圾回收是否涉及到虚拟机栈？"></a>垃圾回收是否涉及到虚拟机栈？</h3><p>不会</p>
<table>
<thead>
<tr>
<th>位置</th>
<th>是否有Error</th>
<th>是否存在GC</th>
</tr>
</thead>
<tbody><tr>
<td>PC计数器</td>
<td>无</td>
<td>不存在</td>
</tr>
<tr>
<td>虚拟机栈</td>
<td>有，SOF</td>
<td>不存在</td>
</tr>
<tr>
<td>本地方法栈(在HotSpot的实现中和虚拟机栈一样)</td>
<td></td>
<td></td>
</tr>
<tr>
<td>堆</td>
<td>有，OOM</td>
<td>存在</td>
</tr>
<tr>
<td>方法区</td>
<td>有</td>
<td>存在</td>
</tr>
</tbody></table>
<h3 id="方法中定义的局部变量是否线程安全？"><a href="#方法中定义的局部变量是否线程安全？" class="headerlink" title="方法中定义的局部变量是否线程安全？"></a>方法中定义的局部变量是否线程安全？</h3><p>具体问题具体分析</p>
<ol>
<li> 如果只有一个线程才可以操作此数据，则必是线程安全的。</li>
<li> 如果有多个线程操作此数据，则此数据是共享数据。如果不考虑同步机制的话，会存在线程安全问题。</li>
</ol>
<p><strong>具体问题具体分析：</strong></p>
<ul>
<li>如果对象是在内部产生，并在内部消亡，没有返回到外部，那么它就是线程安全的，反之则是线程不安全的。</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 面试题：</span></span><br><span class="line"><span class="comment"> * 方法中定义的局部变量是否线程安全？具体情况具体分析</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   何为线程安全？</span></span><br><span class="line"><span class="comment"> *      如果只有一个线程才可以操作此数据，则必是线程安全的。</span></span><br><span class="line"><span class="comment"> *      如果有多个线程操作此数据，则此数据是共享数据。如果不考虑同步机制的话，会存在线程安全问题。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StringBuilderTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> num = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//s1的声明方式是线程安全的（只在方法内部用了）</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">method1</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">//StringBuilder:线程不安全</span></span><br><span class="line">        StringBuilder s1 = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">        s1.append(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">        s1.append(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//sBuilder的操作过程：是线程不安全的（作为参数传进来，可能被其它线程操作）</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">method2</span><span class="params">(StringBuilder sBuilder)</span></span>&#123;</span><br><span class="line">        sBuilder.append(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">        sBuilder.append(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//s1的操作：是线程不安全的（有返回值，可能被其它线程操作）</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> StringBuilder <span class="title">method3</span><span class="params">()</span></span>&#123;</span><br><span class="line">        StringBuilder s1 = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">        s1.append(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">        s1.append(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> s1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//s1的操作：是线程安全的（s1自己消亡了，最后返回的只是s1.toString的一个新对象）</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">method4</span><span class="params">()</span></span>&#123;</span><br><span class="line">        StringBuilder s1 = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">        s1.append(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">        s1.append(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> s1.toString();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        StringBuilder s = <span class="keyword">new</span> StringBuilder();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            s.append(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">            s.append(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        method2(s);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">youthlql</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://imlql.cn/post/5b1b6560.html">https://imlql.cn/post/5b1b6560.html</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://imlql.cn" target="_blank">风祈的时光录</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/JVM/">JVM</a><a class="post-meta__tags" href="/tags/%E8%99%9A%E6%8B%9F%E6%9C%BA/">虚拟机</a></div><div class="post_share"></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" alt="微信"/></a><div class="post-qr-code-desc">微信</div></li><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" alt="支付宝"/></a><div class="post-qr-code-desc">支付宝</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/post/50ac3a1c.html"><img class="prev-cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">JVM系列-第5章-堆</div></div></a></div><div class="next-post pull-right"><a href="/post/a7ad3cab.html"><img class="next-cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">JVM系列-第3章-运行时数据区</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/post/debff71a.html" title="JVM系列-第7章-对象的实例化内存布局与访问定位"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-11-14</div><div class="title">JVM系列-第7章-对象的实例化内存布局与访问定位</div></div></a></div><div><a href="/post/8c954c6.html" title="JVM系列-第1章-JVM与Java体系结构"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-11-02</div><div class="title">JVM系列-第1章-JVM与Java体系结构</div></div></a></div><div><a href="/post/2e0079af.html" title="JVM系列-第2章-类加载子系统"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-11-02</div><div class="title">JVM系列-第2章-类加载子系统</div></div></a></div><div><a href="/post/a7ad3cab.html" title="JVM系列-第3章-运行时数据区"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-11-09</div><div class="title">JVM系列-第3章-运行时数据区</div></div></a></div><div><a href="/post/408712f4.html" title="JVM系列-第8章-执行引擎"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-11-15</div><div class="title">JVM系列-第8章-执行引擎</div></div></a></div><div><a href="/post/d54daa0f.html" title="JVM系列-第10章-垃圾回收概述和相关算法"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/jvm.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-11-16</div><div class="title">JVM系列-第10章-垃圾回收概述和相关算法</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88"><span class="toc-text">虚拟机栈</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%AE%80%E4%BB%8B"><span class="toc-text">简介</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E7%9A%84%E5%87%BA%E7%8E%B0%E8%83%8C%E6%99%AF"><span class="toc-text">虚拟机栈的出现背景</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%86%85%E5%AD%98%E4%B8%AD%E7%9A%84%E6%A0%88%E4%B8%8E%E5%A0%86"><span class="toc-text">内存中的栈与堆</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E5%9F%BA%E6%9C%AC%E5%86%85%E5%AE%B9"><span class="toc-text">虚拟机栈基本内容</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E7%9A%84%E7%89%B9%E7%82%B9"><span class="toc-text">虚拟机栈的特点</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E7%9A%84%E5%BC%82%E5%B8%B8"><span class="toc-text">虚拟机栈的异常</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%AE%BE%E7%BD%AE%E6%A0%88%E5%86%85%E5%AD%98%E5%A4%A7%E5%B0%8F"><span class="toc-text">设置栈内存大小</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5"><span class="toc-text">概念</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BE%E4%BE%8B"><span class="toc-text">举例</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A0%88%E7%9A%84%E5%AD%98%E5%82%A8%E5%8D%95%E4%BD%8D"><span class="toc-text">栈的存储单位</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88%E4%B8%AD%E5%AD%98%E5%82%A8%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="toc-text">栈中存储什么？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86"><span class="toc-text">栈运行原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88%E5%B8%A7%E7%9A%84%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84"><span class="toc-text">栈帧的内部结构</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8"><span class="toc-text">局部变量表</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%AE%A4%E8%AF%86%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8"><span class="toc-text">认识局部变量表</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5-1"><span class="toc-text">概念</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BE%E4%BE%8B-1"><span class="toc-text">举例</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%83%A8%E5%88%86%E8%AF%A6%E8%A7%A3"><span class="toc-text">部分详解</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%B3%E4%BA%8ESlot%E7%9A%84%E7%90%86%E8%A7%A3"><span class="toc-text">关于Slot的理解</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Slot%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B"><span class="toc-text">Slot代码示例</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Slot%E7%9A%84%E9%87%8D%E5%A4%8D%E5%88%A9%E7%94%A8"><span class="toc-text">Slot的重复利用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F%E4%B8%8E%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E7%9A%84%E5%AF%B9%E6%AF%94"><span class="toc-text">静态变量与局部变量的对比</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%A1%A5%E5%85%85%E8%AF%B4%E6%98%8E"><span class="toc-text">补充说明</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88"><span class="toc-text">操作数栈</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88%E7%9A%84%E7%89%B9%E7%82%B9"><span class="toc-text">操作数栈的特点</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88%E7%9A%84%E4%BD%9C%E7%94%A8"><span class="toc-text">操作数栈的作用</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88%E4%BB%A3%E7%A0%81%E8%BF%BD%E8%B8%AA"><span class="toc-text">操作数栈代码追踪</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%80%E6%AD%A5%E4%B8%80%E6%AD%A5%E7%9C%8B%E6%B5%81%E7%A8%8B"><span class="toc-text">一步一步看流程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E9%97%AE%E9%A2%98"><span class="toc-text">小问题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A0%88%E9%A1%B6%E7%BC%93%E5%AD%98%E6%8A%80%E6%9C%AF"><span class="toc-text">栈顶缓存技术</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5"><span class="toc-text">动态链接</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E7%9A%84%E8%B0%83%E7%94%A8"><span class="toc-text">方法的调用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E9%93%BE%E6%8E%A5%E4%B8%8E%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5"><span class="toc-text">静态链接与动态链接</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%97%A9%E6%9C%9F%E7%BB%91%E5%AE%9A%E4%B8%8E%E6%99%9A%E6%9C%9F%E7%BB%91%E5%AE%9A"><span class="toc-text">早期绑定与晚期绑定</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%9A%E6%80%81%E4%B8%8E%E7%BB%91%E5%AE%9A"><span class="toc-text">多态与绑定</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%99%9A%E6%96%B9%E6%B3%95%E4%B8%8E%E9%9D%9E%E8%99%9A%E6%96%B9%E6%B3%95"><span class="toc-text">虚方法与非虚方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BE%E4%BE%8B-2"><span class="toc-text">举例</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%B3%E4%BA%8E-invokedynamic-%E6%8C%87%E4%BB%A4"><span class="toc-text">关于 invokedynamic 指令</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E8%AF%AD%E8%A8%80%E5%92%8C%E9%9D%99%E6%80%81%E8%AF%AD%E8%A8%80"><span class="toc-text">动态语言和静态语言</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Java%E8%AF%AD%E8%A8%80%E4%B8%AD%E6%96%B9%E6%B3%95%E9%87%8D%E5%86%99%E7%9A%84%E6%9C%AC%E8%B4%A8"><span class="toc-text">Java语言中方法重写的本质</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%99%9A%E6%96%B9%E6%B3%95%E8%A1%A8"><span class="toc-text">虚方法表</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E8%BF%94%E5%9B%9E%E5%9C%B0%E5%9D%80"><span class="toc-text">方法返回地址</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%B8%80%E4%BA%9B%E9%99%84%E5%8A%A0%E4%BF%A1%E6%81%AF"><span class="toc-text">一些附加信息</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A0%88%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95%E9%A2%98"><span class="toc-text">栈相关面试题</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BE%E4%BE%8B%E6%A0%88%E6%BA%A2%E5%87%BA%E7%9A%84%E6%83%85%E5%86%B5%EF%BC%9F"><span class="toc-text">举例栈溢出的情况？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B0%83%E6%95%B4%E6%A0%88%E5%A4%A7%E5%B0%8F%EF%BC%8C%E5%B0%B1%E8%83%BD%E4%BF%9D%E8%AF%81%E4%B8%8D%E5%87%BA%E7%8E%B0%E6%BA%A2%E5%87%BA%E4%B9%88%EF%BC%9F"><span class="toc-text">调整栈大小，就能保证不出现溢出么？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%86%E9%85%8D%E7%9A%84%E6%A0%88%E5%86%85%E5%AD%98%E8%B6%8A%E5%A4%A7%E8%B6%8A%E5%A5%BD%E4%B9%88%EF%BC%9F"><span class="toc-text">分配的栈内存越大越好么？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%98%AF%E5%90%A6%E6%B6%89%E5%8F%8A%E5%88%B0%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%EF%BC%9F"><span class="toc-text">垃圾回收是否涉及到虚拟机栈？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E4%B8%AD%E5%AE%9A%E4%B9%89%E7%9A%84%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E6%98%AF%E5%90%A6%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%EF%BC%9F"><span class="toc-text">方法中定义的局部变量是否线程安全？</span></a></li></ol></li></ol></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By youthlql</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text"><a href="https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral" target="_blank" rel="noopener" class="one-pan-link-mark"><img style="position:relative;top:-3px; " src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/upyun/logo.png" align="absmiddle" width="60px" height="30px"></a><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn"><img class="icp-icon" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/icp.png"><span>鄂ICP备19028890号</span></a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">简</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">本地搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.umd.js"></script><script src="https://npm.elemecdn.com/instant.page/instantpage.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script defer src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script></div></body></html>